/**method of response
 * @author lms 2017.03.30
 */
package ms.core.common.utils;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;

import ms.core.common.AppException;
import ms.core.common.MediaType;
import ms.core.common.consts.SysErr;
import ms.core.common.utils.UploadedFileUtils.Annex;
import ms.core.common.utils.UploadedFileUtils.FileDoc;
import ms.core.tool.JsonTool;
import ms.core.tool.Log4j2Tool;
import ms.core.tool.StrTool;
import ms.core.tool.SysTool;

public class RequestUtils {

	public static MediaType getMediaType(HttpServletRequest request) {
		String contentType = request.getContentType();
		if (!StrTool.isBlank(contentType)) {
			return MediaType.parse(contentType);
		}

		return null;
	}

	/**
	 * 从 HttpServletRequest 对象，获取 query 参数
	 * @param request
	 * @return
	 */
	public static String getRequestQueryString(HttpServletRequest request){
		String ret = "";
		Map<String, String[]> qryParams = request.getParameterMap();
		for (String key: qryParams.keySet()) {
            String[] values = qryParams.get(key);
            for (int i=0; i<values.length; i++) {
                String value = values[i];
                ret += key+"="+value+"&";
            }
        }
		if (ret.length()>0) ret = StrTool.deleteCharAt(ret, ret.length()-1);
		return ret;
	}
	
	/**
	 * 判断上传附件类型、单文件大小、文件总数是否合规，若合规，返回文件数量
	 * 
	 * @param files
	 * @param fileFilters 如 ".jpg;.jpeg;.png;.xls"
	 * @param fileMaxSize 单位kb
	 * @param fileMaxNum
	 * @return (>=0成功;<0失败)
	 */
	private static int verifyFileItems(List<FileItem> files, String fileFilters, long fileMaxSize, int fileMaxNum){
		if (StrTool.isBlank(fileFilters)) fileFilters = ".*";
		List<String> filters = StrTool.splitString(fileFilters.toLowerCase(), ";");

		int fileNum = 0;
		boolean success = true;
		for(FileItem item: files) {
			if (!item.isFormField()) {
				String fileName = item.getName();
				String fileExt = SysTool.getFileExt(fileName);
				// 文件格式校验
				if (filters.indexOf(".*")<0 && filters.indexOf(fileExt)<0) {
					success = false;
					break;
				}
				// 文件大小校验
				if (item.getSize() > fileMaxSize * 1024) {
					success = false;
					break;
				}
				// 附件数校验
				fileNum ++;
				if (fileNum>fileMaxNum) {
					success = false;
					break;
				}
			}

			if (!success) break;
		}
		
		// 如果校验失败，删除文件列表
		if (!success) {
			for(FileItem item: files) item.delete();
			files.clear();
		}

		return success ? fileNum : -1;
	}

	/**
	 * 删除上传附件
	 * 
	 * @param list
	 */
	public static void deleteAnnexs(List<FileDoc> list) {
		if (list==null || list.size()==0)
			return;
		
		for (int i=0; i<list.size(); i++) {
			SysTool.deleteFile(list.get(i).getFile());
		}
	}

	/**
	 * 获取上传附件及参数
	 * 
	 * @param req http请求对象
	 * @param cachePath 临时缓存路径
	 * @param uploadPath 上传保存目录
	 * @param fileFilters 文件过滤器
	 * @param fileMaxSize 单文件最大限制(kb)
	 * @param fileMaxNum 一次上传附件最大数量
	 * @return
	 */
	public static Map<String, Object> getMultipartBody(HttpServletRequest req, String cachePath, String uploadPath,
													   String fileFilters, long fileMaxSize, int fileMaxNum) {
		// 设置附件上传临时缓存目录
		if (StrTool.isBlank(cachePath))
			cachePath = req.getServletContext().getRealPath("/WEB-INF/temp");
		if (SysTool.dirExists(cachePath, true) != 1)
			throw new AppException(SysErr.FILE_ACCESS_ERR, "创建临时缓存上传文件目录失败!");

		if (SysTool.dirExists(uploadPath, true) != 1)
			throw new AppException(SysErr.FILE_ACCESS_ERR, "创建保存上传文件目录失败!");

		Map<String, Object> ret = new HashMap<>();
		// files 存访上传后的文件列表
		List<Annex> list = new ArrayList<>();
		ret.put("files", list);

		try {
			// 1. create DiskFileItemFactory, set cache size and temp path
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			// 1.1 设置内存缓存附件临界值为10k，超过存磁盘(设置10k，目的是让文件保存于临时文件)
			diskFileItemFactory.setSizeThreshold(10240);
			File tmpFile = new File(cachePath);
			diskFileItemFactory.setRepository(tmpFile);

			// 2. create file upload servlet and upload progress listener
			ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
			servletFileUpload.setHeaderEncoding("UTF-8");
			servletFileUpload.setProgressListener(new ProgressListener() {
				public void update(long pBytesRead, long pContentLength, int pItems) {
				}
			});

			// 2.1 获取文件列表，list 保存了客户端上传附件内容列表，此操作后附件会写入磁盘缓存
			List<FileItem> files = servletFileUpload.parseRequest(req);
			if (files==null || files.size()==0)
				throw new AppException(SysErr.SYS_PARAM_ERR, "未上传文件!");

			int fileCount = verifyFileItems(files, fileFilters, fileMaxSize, fileMaxNum);
			if (fileCount<0)
				throw new AppException(SysErr.SYS_PARAM_ERR, "文件上传受限，如文件类型、文件数或单文件大小不符!");

			for(FileItem item: files) {
				if (item.isFormField()) {
					ret.put(item.getFieldName(), item.getString("utf8"));
				} else {
					String fileExt = SysTool.getFileExt(item.getName());
					String fullName = uploadPath+"/"+SysTool.makeUUID("")+fileExt;
					File file = new File(fullName); 
					if (file.exists()) file.delete();
					item.write(file);

					Annex annex = new Annex();
					annex.setFieldName(item.getFieldName());	// 字段名
					annex.setSrc(item.getName());				// 原始文件名
					annex.setDst(fullName);						// 上传后保存的文件带全路径名
					list.add(annex);
				}
				item.delete();
			}
		} catch (Exception e) {
			Log4j2Tool.error(RequestUtils.class, e);
			if (e instanceof AppException) throw new AppException(e);
			else throw new AppException(SysErr.SYS_UPLOAD_ERR, "获取附件上传表单参数失败");
		}

		return ret;
	}
	
	/**
	 * 获取表单参数，并转成json对象
	 * 
	 * @param req
	 * @return
	 */
	public static Map<String, Object> getFormBody(HttpServletRequest req) {
		Map<String, Object> ret = new HashMap<>();
		Map<String,String[]> params = req.getParameterMap();
		if (params==null) 
			return ret;

		//参数名称
		Set<String> key = params.keySet();
		//参数迭代器
		Iterator<String> iterator = key.iterator();
		while(iterator.hasNext()){
			String k=iterator.next();
			ret.put(k, params.get(k)[0]);
		}

		return ret;
	}

	/**
	 * 获取提交的json数据
	 * 
	 * @param req
	 * @return
	 */
	public static Map<String, Object> getJsonBody(HttpServletRequest req) {
		Map<String, Object> ret = null;
		try {
    		InputStream is = req.getInputStream();
            if (is!=null) {
            	String body = IOUtils.toString(is, "utf-8");
            	ret = JsonTool.objectToMap(body, false);
            }
        } catch (Exception e) {
        	throw new AppException(SysErr.SYS_PARAM_ERR, "无效JSON格式参数!");
        }
        return ret;
	}

	/**
	 * 从HttpServletRequest 对象获取访问者ip
	 * @param request
	 * @return
	 */
	public static String getRequestRemoteIp(HttpServletRequest request){
	    String ip = null;

	    //X-Forwarded-For：squid 服务代理
	    ip = request.getHeader("X-Forwarded-For");
	    if (StrTool.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
	        //Proxy-Client-IP：apache 服务代理
	        ip = request.getHeader("Proxy-Client-IP");
	    }

	    if (StrTool.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
	        //WL-Proxy-Client-IP：weblogic 服务代理
	        ip = request.getHeader("WL-Proxy-Client-IP");
	    }

	    if (StrTool.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
	        //HTTP_CLIENT_IP：有些代理服务器
	        ip = request.getHeader("HTTP_CLIENT_IP");
	    }

	    if (StrTool.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
	        //X-Real-IP：nginx服务代理
	        ip = request.getHeader("X-Real-IP");
	    }

	    //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
	    if (!StrTool.isBlank(ip)) {
	        ip = ip.split(",")[0];
	    }

	    //还是不能获取到，最后再通过request.getRemoteAddr();获取
	    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
	        ip = request.getRemoteAddr();
	    }
	    return ip;
	}
}
